/**
 * Copyright (C) 2010-2016 eBusiness Information, Excilys Group
 * Copyright (C) 2016-2020 the AndroidAnnotations project
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed To in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.ohosannotations.holder;

import com.helger.jcodemodel.AbstractJClass;
import com.helger.jcodemodel.IJExpression;
import com.helger.jcodemodel.JBlock;
import com.helger.jcodemodel.JExpr;
import com.helger.jcodemodel.JFieldVar;
import com.helger.jcodemodel.JInvocation;
import com.helger.jcodemodel.JMethod;
import com.helger.jcodemodel.JMod;
import com.helger.jcodemodel.JVar;

import org.ohosannotations.OhosAnnotationsEnvironment;

import java.util.ArrayList;
import java.util.List;

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;

import static com.helger.jcodemodel.JExpr._super;
import static com.helger.jcodemodel.JExpr.invoke;
import static com.helger.jcodemodel.JMod.PRIVATE;
import static com.helger.jcodemodel.JMod.PUBLIC;
import static com.helger.jcodemodel.JMod.STATIC;
import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
import static org.ohosannotations.helper.ModelConstants.generationSuffix;

/**
 * EViewHolder
 *
 * @author dev
 * @since 2021-07-21
 */
public class EViewHolder extends EComponentWithViewSupportHolder implements HasInstanceState, HasReceiverRegistration {
    /**
     * 已经膨胀的评论
     */
    protected static final String ALREADY_INFLATED_COMMENT = ""
        + "The alreadyInflated_ hack is needed because of an Ohos bug" + System.lineSeparator()
        + "which leads to infinite calls of onFinishInflate()" + System.lineSeparator()
        + "when inflating an layout with an parent and using" + System.lineSeparator()
        + "the <code>&lt;merge /&gt;</code> tag.";

    private static final String SUPPRESS_WARNING_COMMENT = "" //
        + "We use @SuppressWarning here because our java code" + System.lineSeparator() //
        + "generator doesn't know that there is no need" + System.lineSeparator() //
        + "to import OnXXXListeners from View as we already" + System.lineSeparator() //
        + "are in an View.";

    /**
     * init的身体
     */
    protected JBlock initBody;
    /**
     * 在完成充气
     */
    protected JMethod onFinishInflate;
    /**
     * 已经膨胀
     */
    protected JFieldVar alreadyInflated;
    private JMethod onAttachedToWindowMethod;
    private JBlock onAttachedToWindowAfterSuperBlock;
    private JMethod onDetachedFromWindowMethod;
    private JBlock onDetachedFromWindowBeforeSuperBlock;
    private ReceiverRegistrationDelegate<EViewHolder> receiverRegistrationDelegate;
    private ViewInstanceStateDelegate instanceStateDelegate;

    /**
     * EViewHolder
     *
     * @param environment 环境
     * @param annotatedElement 带注释的元素
     * @throws Exception 异常
     */
    public EViewHolder(OhosAnnotationsEnvironment environment, TypeElement annotatedElement) throws Exception {
        super(environment, annotatedElement);
        addSuppressWarning();
        createConstructorAndBuilder();
        receiverRegistrationDelegate = new ReceiverRegistrationDelegate<>(this);
        instanceStateDelegate = new ViewInstanceStateDelegate(this);
    }

    /**
     * 添加抑制警告
     */
    private void addSuppressWarning() {
        generatedClass.javadoc().append(SUPPRESS_WARNING_COMMENT);

        codeModelHelper.addSuppressWarnings(getGeneratedClass(), "unused");
    }

    /**
     * 创建构造函数和建设者
     */
    private void createConstructorAndBuilder() {
        List<ExecutableElement> constructors = new ArrayList<>();
        for (Element e1 : annotatedElement.getEnclosedElements()) {
            if (e1.getKind() == CONSTRUCTOR) {
                constructors.add((ExecutableElement) e1);
            }
        }

        for (ExecutableElement userConstructor : constructors) {
            JMethod copyConstructor = generatedClass.constructor(PUBLIC);
            JMethod staticHelper = generatedClass.method(PUBLIC | STATIC, generatedClass._extends(), "build");

            codeModelHelper.generify(staticHelper, getAnnotatedElement());

            JBlock body = copyConstructor.body();
            JInvocation superCall = body.invoke("super");
            AbstractJClass narrowedGeneratedClass = narrow(generatedClass);

            JInvocation newInvocation = JExpr._new(narrowedGeneratedClass);
            for (VariableElement param : userConstructor.getParameters()) {
                String paramName = param.getSimpleName().toString();
                AbstractJClass paramType = codeModelHelper.typeMirrorToJClass(param.asType());
                copyConstructor.param(paramType, paramName);
                staticHelper.param(paramType, paramName);
                superCall.arg(JExpr.ref(paramName));
                newInvocation.arg(JExpr.ref(paramName));
            }

            JVar newCall = staticHelper.body().decl(narrowedGeneratedClass, "instance", newInvocation);
            staticHelper.body().invoke(newCall, getOnFinishInflate());
            staticHelper.body()._return(newCall);
            body.invoke(getInit());
        }
    }

    /**
     * 设置上下文裁判
     */
    @Override
    protected void setContextRef() {
        contextRef = invoke("getContext");
    }

    /**
     * 设置初始化
     */
    @Override
    protected void setInit() {
        init = generatedClass.method(PRIVATE, getCodeModel().VOID, "init" + generationSuffix());
        viewNotifierHelper.wrapInitWithNotifier();
    }

    /**
     * getInitBody
     *
     * @return {@link JBlock}
     */
    @Override
    public JBlock getInitBody() {
        if (initBody == null) {
            setInit();
        }
        return initBody;
    }

    /**
     * 设置初始化的身体
     *
     * @param initBody init的身体
     */
    public void setInitBody(JBlock initBody) {
        this.initBody = initBody;
    }

    /**
     * 在完成充气
     *
     * @return {@link JMethod}
     */
    public JMethod getOnFinishInflate() {
        if (onFinishInflate == null) {
            setOnFinishInflate();
        }
        return onFinishInflate;
    }

    /**
     * 在完成充气
     */
    protected void setOnFinishInflate() {
        onFinishInflate = generatedClass.method(PUBLIC, getCodeModel().VOID, "onFinishInflate");
        onFinishInflate.javadoc().append(ALREADY_INFLATED_COMMENT.replaceAll("alreadyInflated_",
            "alreadyInflated" + generationSuffix()));

        JBlock ifNotInflated = onFinishInflate.body()._if(getAlreadyInflated().not())._then();
        ifNotInflated.assign(getAlreadyInflated(), JExpr.TRUE);

        getInit();
        viewNotifierHelper.invokeViewChanged(ifNotInflated);
    }

    /**
     * 得到已经膨胀
     *
     * @return {@link JFieldVar}
     */
    public JFieldVar getAlreadyInflated() {
        if (alreadyInflated == null) {
            setAlreadyInflated();
        }
        return alreadyInflated;
    }

    /**
     * 超级块后开始生命周期
     *
     * @return {@link JBlock}
     */
    @Override
    public JBlock getStartLifecycleAfterSuperBlock() {
        return getOnAttachedToWindowAfterSuperBlock();
    }

    /**
     * 超级块之前把生命周期结束
     *
     * @return {@link JBlock}
     */
    @Override
    public JBlock getEndLifecycleBeforeSuperBlock() {
        return getOnDetachedToWindowBeforeSuperBlock();
    }

    /**
     * 通过id获取找到视图表达式
     *
     * @param idParam id参数
     * @return {@link IJExpression}
     */
    @Override
    public IJExpression getFindViewByIdExpression(JVar idParam) {
        return JExpr._this().invoke("findComponentById").arg(idParam);
    }

    /**
     * 得到目的过滤字段
     *
     * @param intentFilterData 目的筛选数据
     * @return {@link JFieldVar}
     */
    @Override
    public JFieldVar getIntentFilterField(ReceiverRegistrationDelegate.IntentFilterData intentFilterData) {
        return receiverRegistrationDelegate.getIntentFilterField(intentFilterData);
    }

    /**
     * 得到意图过滤器初始化块
     *
     * @param intentFilterData 目的筛选数据
     * @return {@link JBlock}
     */
    @Override
    public JBlock getIntentFilterInitializationBlock(ReceiverRegistrationDelegate.IntentFilterData intentFilterData) {
        return getInitBodyInjectionBlock();
    }

    /**
     * 超级块后附加到窗口
     *
     * @return {@link JBlock}
     */
    protected JBlock getOnAttachedToWindowAfterSuperBlock() {
        if (onAttachedToWindowAfterSuperBlock == null) {
            setOnAttachedToWindow();
        }
        return onAttachedToWindowAfterSuperBlock;
    }

    /**
     * 分离的窗口前超级块
     *
     * @return {@link JBlock}
     */
    protected JBlock getOnDetachedToWindowBeforeSuperBlock() {
        if (onDetachedFromWindowBeforeSuperBlock == null) {
            setOnDetachedFromWindow();
        }
        return onDetachedFromWindowBeforeSuperBlock;
    }

    /**
     * 设置已经膨胀
     */
    private void setAlreadyInflated() {
        alreadyInflated = generatedClass.field(PRIVATE, getCodeModel().BOOLEAN,
            "alreadyInflated" + generationSuffix(), JExpr.FALSE);
    }

    /**
     * 设置窗口
     */
    private void setOnAttachedToWindow() {
        onAttachedToWindowMethod = generatedClass.method(JMod.PUBLIC, getCodeModel().VOID, "onAttachedToWindow");
        onAttachedToWindowMethod.annotate(Override.class);
        JBlock body = onAttachedToWindowMethod.body();
        body.invoke(_super(), onAttachedToWindowMethod);
        onAttachedToWindowAfterSuperBlock = body.blockSimple();
    }

    /**
     * 在脱离窗口
     */
    private void setOnDetachedFromWindow() {
        onDetachedFromWindowMethod = generatedClass.method(JMod.PUBLIC, getCodeModel().VOID, "onDetachedFromWindow");
        onDetachedFromWindowMethod.annotate(Override.class);
        JBlock body = onDetachedFromWindowMethod.body();
        onDetachedFromWindowBeforeSuperBlock = body.blockSimple();
        body.invoke(_super(), onDetachedFromWindowMethod);
    }

    /**
     * 保存状态方法主体
     *
     * @return {@link JBlock}
     */
    @Override
    public JBlock getSaveStateMethodBody() {
        return instanceStateDelegate.getSaveStateMethodBody();
    }

    /**
     * 保存状态包参数
     *
     * @return {@link JVar}
     */
    @Override
    public JVar getSaveStateBundleParam() {
        return instanceStateDelegate.getSaveStateBundleParam();
    }

    /**
     * 恢复状态的方法
     *
     * @return {@link JMethod}
     */
    @Override
    public JMethod getRestoreStateMethod() {
        return instanceStateDelegate.getRestoreStateMethod();
    }

    /**
     * 让身体恢复状态方法
     *
     * @return {@link JBlock}
     */
    @Override
    public JBlock getRestoreStateMethodBody() {
        return instanceStateDelegate.getRestoreStateMethodBody();
    }

    /**
     * 恢复状态包参数
     *
     * @return {@link JVar}
     */
    @Override
    public JVar getRestoreStateBundleParam() {
        return instanceStateDelegate.getRestoreStateBundleParam();
    }
}
